<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <!-- 创建期约实例 -->
    <!--     <script>
        let p1 = new Promise((resolve, reject) => resolve());
        setTimeout(console.log, 0, `p1=${p1}`);
        let p2 = new Promise((resolve, reject) => setTimeout(resolve, 1000));
        setTimeout(console.log, 0, p2);
        setTimeout(console.log, 1000, p2);

        /* Promise.resolve() - 实例化一个解决的期约 */
        {
            let p1new = Promise.resolve(4);
            setTimeout(console.log, 2000, p1new);
        }

        /* Promise.reject() - 实例化一个拒绝的期约 */
        {
            let p2new = Promise.reject();
            setTimeout(console.log, 3000, p2new);
        }
    </script> -->

    <!-- 期约实例方法 -->
    <script>
        /* Promise.prototype.then() */
        {
            // 创建不同状态不同时间的Promise实例函数
            const timeout = function (ms, state) {
                if (state === 'resolve')
                    return new Promise((resolve, reject) => {
                        setTimeout(resolve, ms);
                    });
                else if (state === 'reject')
                    return new Promise((resolve, reject) => {
                        setTimeout(reject, ms);
                    });
            }
            const onResolved = (id) => { console.log(id, 'resolve'); };
            const onRejected = (id) => { console.log(id, 'reject'); };
            timeout(1000, 'resolve').then(() => onResolved(1), () => onRejected(1));
            timeout(1000, 'reject').then(() => onResolved(2), () => onRejected(2));
            //做一些异步操作
            let p = new Promise((resolve, reject) => {
                setTimeout(function () {
                    var num = Math.ceil(Math.random() * 10); //生成1-10的随机数
                    if (num <= 5) {
                        resolve(num);
                    }
                    else {
                        reject('数字太大了');
                    }
                }, 2000);
            });
            p.then((data) => {
                console.log('resolved', data);
            }, (err) => {
                console.log('rejected', err);
            });
        }

        /* Promise.prototype.catch() */
        {
            // === Promise.prototype.then(null, onRejected)
            let p1 = Promise.reject('reject - catch test');
            let p2 = p1.catch((re) => { setTimeout(console.log, 3000, re) });
        }

        /* Promise.prototype.finally() */
        {
            const fin = function () {
                setTimeout(() => { console.log('finally'); }, 4000);
            };
            let p1 = Promise.resolve();
            // let p2 = Promise.reject('11');
            // p2.finally(fin);
            p1.finally(fin);
        }

        /* Promise.all() */
        {
            // 有一个包含的期约为pending/reject, 则合成期约pending/reject
            const p = Promise.all([
                Promise.resolve('all1'),
                // Promise.reject(),
                Promise.resolve('all2')]);
            setTimeout(console.log, 5000, p);
        }

        /* Promise.race() */
        {
            // 合成期约状态为第一个落定的期约状态
            const p = Promise.race([
                Promise.resolve('race'),
                new Promise((resolve, reject) => { setTimeout(reject, 1000); }),
                // new Promise((resolve, reject) => { setTimeout(resolve, 2000);})
            ]);
            setTimeout(console.log, 6000, p);
        }

        /* Promise.allSettled() */
        {
            // Promise.all()的优化, 确定一组一步操作是否都结束了
            // 无论参数是否reject, allSettled内部都会resolve, 但添加状态status来记录参数实例是否reject
            const p = Promise.allSettled([
                new Promise((resolve, reject) => { setTimeout(() => { reject('p1') }, 2000); }),
                new Promise((resolve, reject) => { setTimeout(() => { resolve('p2') }, 3000); }),
                Promise.resolve('p3'),
                Promise.reject('p4')
            ]);
            // setTimeout(console.log, 0, p, 'Promise.allSettled');
            p.then((args) => { setTimeout(console.log, 4000, args); }); // 返回包含status和value/reason的一组对象
            // 4s后开始执行, 实例需要的最大时间为3s, 实际在7s后输出args
        }

        /* Promise.any() */
        {
            // Promise.race()的优化, 不会因为某个promise变成reject结束
            // 一个为fulfilled, 则为fulfilled; 所有都为rejected, 则为rejected
            const p1 = Promise.any([
                Promise.resolve('fullied'),
                Promise.resolve('fullied'),
                Promise.reject('rejected')
            ]);
            const p2 = Promise.any([
                Promise.reject('rejected'),
                Promise.reject('rejected'),
                Promise.reject('rejected')
            ]);
            p1.then((args) => { setTimeout(console.log, 8000, args); });
            p2.catch((args) => { setTimeout(console.log, 8000, args.errors); });
        }

        /* Promise.try() */
        {
            // 使用Promise 库Bluebird、Q等，或引入Polyfill
            // 统一管理同步/异步操作, 更好的管理异常, 模拟try代码块
            const p = Promise.resolve({ id: 'try' });
            // 抛出异步错误
            p.then((args) => { console.log(args); })
                .catch(() => { console.log('error'); });
            // 抛出同步&同步错误
            try {
                p.then((args) => { console.log(args); })
                    .catch(() => { console.log('error'); });
            } catch (e) { }
            // 改良写法
            /* 
                Promise.try(() => { return p; })
                    .then((args) => { console.log(args); })
                    .catch(() => { console.log('error'); });
            */
        }
    </script>

    <!-- 串行期约合成 -->
    <script>
        {
            function addTwo(x) { return x + 2; }
            function addThree(x) { return x + 3; }
            function addFive(x) { return x + 5; }

            // 组合函数合成
            function addTen1(x) { return addFive(addThree(addTwo(x))); }
            console.log(addTen1(1));

            // 串行期约合成, 渐进消费一个值
            function addTen2(x) { return Promise.resolve(x).then(addTwo).then(addThree).then(addFive); }
            addTen2(2).then(console.log);

            // 利用Array.prototype.reduce()写的更简洁
            function addTen3(x) {
                return [addTwo, addThree, addFive]
                    .reduce((promise, fn) => promise.then(fn), Promise.resolve(x));
            }
            addTen3(3).then(console.log);

            // 提炼出通用函数
            function compose(...fns) {
                return (x) => fns.reduce((promise, fn) => promise.then(fn), Promise.resolve(x));
            }
            let addTen4 = compose(addTwo, addThree, addFive);
            addTen4(4).then(console.log);
        }
    </script>
</body>

</html>